from tkinter import *
from tkinter.scrolledtext import *

import time
import datetime
import os

from backtest import *

EXCEL_PATH = "\"C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE\""


class MY_GUI():
    def __init__(self, init_window):
        self.init_window = init_window
        self.stockVar = StringVar()
        self.startVar = StringVar()
        self.endVar = StringVar()
        self.periodVar = IntVar()
        self.maInVar = IntVar()
        self.maOutVar = IntVar()
        self.ruleVar = IntVar()
        self.init_inputs()

    def validateStock(self):
        stock = self.stockVar.get()
        if len(stock) == 6 and int(stock) != None:
            return True
        else:
            self.write_log_to_Text("输入的股票代码无效！")
            return False

    def validateStart(self):
        start = self.startVar.get()
        if len(start) == 8 and datetime.datetime.strptime(start, "%Y%m%d") != None:
            return True
        else:
            self.write_log_to_Text("输入的起始日期无效！")
            return False

    def validateEnd(self):
        end = self.endVar.get()
        if len(end) == 8 and datetime.datetime.strptime(end, "%Y%m%d") != None:
            end = datetime.datetime.strptime(end, "%Y%m%d")
            start = datetime.datetime.strptime(self.startVar.get(), "%Y%m%d")
            if end >= start:
                return True
            else:
                self.write_log_to_Text("输入的结束日期无效（需要晚于起始日期）！")
                return False
        else:
            self.write_log_to_Text("输入的结束日期无效！")
            return False

    def set_init_window(self):
        """设置窗口
        """
        self.init_window.title("量化回测工具 v1.0")
        self.init_window.geometry("+600+20")
        self.init_window.resizable(0, 0)
        self.init_window.iconbitmap("tt.ico")

        # 输入部分
        Label(self.init_window, text="股票代码（e.g. 000001）").grid(row=1, sticky=E)
        Label(self.init_window, text="开始日期（e.g. 20170101）").grid(row=2, sticky=E)
        Label(self.init_window, text="结束日期（e.g. 20170101）").grid(row=3, sticky=E)

        Entry(self.init_window, textvariable=self.stockVar, validate="focusout",
              validatecommand=self.validateStock).grid(row=1, column=1, sticky=W, columnspan=3, padx=10, pady=10)
        Entry(self.init_window, textvariable=self.startVar, validate="focusout",
              validatecommand=self.validateStart).grid(row=2, column=1, sticky=W, columnspan=3, padx=10, pady=10)
        Entry(self.init_window, textvariable=self.endVar, validate="focusout",
              validatecommand=self.validateEnd).grid(row=3, column=1, sticky=W, columnspan=3, padx=10, pady=10)

        Label(self.init_window, text="周期（e.g. 15/30/60/120/240）").grid(row=4, sticky=E)
        Radiobutton(self.init_window, text="15", variable=self.periodVar, value=15).grid(row=4, column=1, sticky=W, padx=10)
        Radiobutton(self.init_window, text="30", variable=self.periodVar, value=30).grid(row=4, column=2, sticky=W, padx=10)
        Radiobutton(self.init_window, text="60", variable=self.periodVar, value=60).grid(row=4, column=3,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="120", variable=self.periodVar, value=120).grid(row=5, column=1,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="240", variable=self.periodVar, value=240).grid(row=5, column=2, sticky=W, padx=10)

        Label(self.init_window, text="买入MA值（e.g. 5/10/20/30/60）").grid(
            row=6, sticky=E)
        Radiobutton(self.init_window, text="5", variable=self.maInVar, value=5).grid(row=6, column=1, sticky=W, padx=10)
        Radiobutton(self.init_window, text="10", variable=self.maInVar, value=10).grid(row=6, column=2,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="20", variable=self.maInVar, value=20).grid(row=6, column=3,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="30", variable=self.maInVar, value=30).grid(row=7, column=1, sticky=W, padx=10)
        Radiobutton(self.init_window, text="60", variable=self.maInVar, value=60).grid(row=7, column=2, sticky=W,  padx=10)

        Label(self.init_window, text="卖出MA值（e.g. 5/10/20/30/60）").grid(
            row=8, sticky=E)
        Radiobutton(self.init_window, text="5", variable=self.maOutVar, value=5).grid(row=8, column=1, sticky=W, padx=10)
        Radiobutton(self.init_window, text="10", variable=self.maOutVar, value=10).grid(row=8, column=2,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="20", variable=self.maOutVar, value=20).grid(row=8, column=3,  sticky=W, padx=10)
        Radiobutton(self.init_window, text="30", variable=self.maOutVar, value=30).grid(row=9, column=1, sticky=W, padx=10)
        Radiobutton(self.init_window, text="60", variable=self.maOutVar, value=60).grid(row=9, column=2, sticky=W,  padx=10)

        Label(self.init_window, text="策略规则").grid(row=10, sticky=E)
        Radiobutton(self.init_window, text="规则1", variable=self.ruleVar, value=1).grid(row=10, column=1, sticky=W, padx=10)

        # 执行部分
        Button(self.init_window, text="重置输入", bg="lightgrey", command=self.init_inputs).grid(
            row=11, column=0, padx=10, pady=10, sticky=E)
        Button(self.init_window, text="执行回测", bg="lightblue", command=self.run_backtester).grid(
            row=11, column=1, padx=10, pady=10, sticky=W)

        # 输出部分
        self.result_data_Text = ScrolledText(self.init_window)
        self.result_data_Text.grid(rowspan=15, columnspan=4, padx=10, pady=10)

    def init_inputs(self):
        self.stockVar.set('000001')
        self.startVar.set('20211021')
        self.endVar.set('20211029')
        self.maInVar.set(5)
        self.maOutVar.set(5)
        self.periodVar.set(30)
        self.ruleVar.set(1)

    def run_backtester(self):
        """回测执行函数
        """

        # 获取参数
        stock = self.stockVar.get()
        start = self.startVar.get()
        end = self.endVar.get()
        period = self.periodVar.get()
        ma_in = self.maInVar.get()
        ma_out = self.maOutVar.get()
        rule = self.ruleVar.get()

        self.write_log_to_Text(
            'Backtester started(%s %s-%s %dm MA%d MA%d Rule%d)...' % (stock, start, end, period, ma_in, ma_out,rule))
        b = BackTester(stock, start, end, period, ma_in, ma_out, rule, self.write_log_to_Text)

        # 获取数据集
        if b.prepareData():
            self.write_log_to_Text('Prepared data...')

        # 执行
        if b.execTransactions():
            self.write_log_to_Text("Exec transactions...Total: %d Success: %d Fail: %d" % (
                b.count, b.count_s, b.count_f))

        self.write_log_to_Text('Backtester Finished...')

        # 统计结果
        if b.count != 0:
            success_rate = float(b.count_s/b.count)
            total_rate_of_return = b.output['rate_of_return'].sum()
            average_rate_of_return = b.output['rate_of_return'].mean()
            self.result_data_Text.insert(END, '总交易次数:%d 成功率:%.2f%% 总收益率:%.2f%% 平均收益率:%.2f%% ' % (b.count, success_rate*100, total_rate_of_return*100, average_rate_of_return*100))
            result = pd.DataFrame([[b.count, success_rate, total_rate_of_return, average_rate_of_return]], columns=['total_count_of_transactions', 'success_rate', 'total_rate_of_return', 'average_rate_of_return'])
            result.to_csv(".tmp/output_%s_%s_%s_%d_%d_%d_%d.csv" % (stock, start, end, period, ma_in, ma_out, rule), mode='a')
        else:
            self.result_data_Text.insert(END, '成功率:NA ')

        self.result_data_Text.tag_configure(
            "outputfile", foreground="blue", underline=True)
        self.result_data_Text.tag_bind('outputfile', '<Button-1>',
            lambda event: os.system(EXCEL_PATH + " .tmp/output_%s_%s_%s_%d_%d_%d_%d.csv" % (stock, start, end, period, ma_in, ma_out, rule)))
        self.result_data_Text.insert(END, "结果文件", "outputfile")

        self.result_data_Text.insert(END, " ")
        self.result_data_Text.tag_configure(
            "datafile", foreground="orange", underline=True)
        self.result_data_Text.tag_bind('datafile', '<Button-1>',
            lambda event: os.system(EXCEL_PATH + " .tmp/data_%s_%s_%s_%d_%d_%d.csv" % (stock, start, end, period, ma_in, ma_out)))
        self.result_data_Text.insert(END, "数据集", "datafile")

        self.result_data_Text.insert(END, '\n')
        self.result_data_Text.update()
        self.result_data_Text.see(END)

    def write_log_to_Text(self, logmsg):
        """打印日志
        """
        current_time = time.strftime('%H:%M:%S', time.localtime(time.time()))
        logmsg_in = str(current_time) + " " + str(logmsg) + "\n"  # 换行
        self.result_data_Text.insert(END, logmsg_in)
        self.result_data_Text.update()
        self.result_data_Text.see(END)


def gui_start():
    BT_PORTAL = MY_GUI(Tk())
    BT_PORTAL.set_init_window()
    BT_PORTAL.init_window.mainloop()


gui_start()
